<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">

/* Stylesheet for pygments enhanced reStructured Text */
/* ================================================== */

/* :Author:    Guenter Milde */
/* :Copyright: 2007 G. Milde */
/*             This stylesheet is released under the GPL v. 2 or later */

/* This stylesheet provides syntax highlight for documents generated with a */
/* pygments_ enhanced reStructured Text -> html converter. */

/* Import the default docutils style sheet */
/* --------------------------------------- */
/* :: */

/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 5196 2007-06-03 20:25:28Z wiemann $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.

See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/

/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
  border: 0 }

table.borderless td, table.borderless th {
  /* Override padding for "table.docutils td" with "! important".
     The right padding separates the table cells. */
  padding: 0 0.5em 0 0 ! important }

.first {
  /* Override more specific margin styles with "! important". */
  margin-top: 0 ! important }

.last, .with-subtitle {
  margin-bottom: 0 ! important }

.hidden {
  display: none }

a.toc-backref {
  text-decoration: none ;
  color: black }

blockquote.epigraph {
  margin: 2em 5em ; }

dl.docutils dd {
  margin-bottom: 0.5em }

/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
  font-weight: bold }
*/

div.abstract {
  margin: 2em 5em }

div.abstract p.topic-title {
  font-weight: bold ;
  text-align: center }

div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
  margin: 2em ;
  border: medium outset ;
  padding: 1em }

div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
  font-weight: bold ;
  font-family: sans-serif }

div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
  color: red ;
  font-weight: bold ;
  font-family: sans-serif }

/* Uncomment (and remove this text!) to get reduced vertical space in
   compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
  margin-bottom: 0.5em }

div.compound .compound-last, div.compound .compound-middle {
  margin-top: 0.5em }
*/

div.dedication {
  margin: 2em 5em ;
  text-align: center ;
  font-style: italic }

div.dedication p.topic-title {
  font-weight: bold ;
  font-style: normal }

div.figure {
  margin-left: 2em ;
  margin-right: 2em }

div.footer, div.header {
  clear: both;
  font-size: smaller }

div.line-block {
  display: block ;
  margin-top: 1em ;
  margin-bottom: 1em }

div.line-block div.line-block {
  margin-top: 0 ;
  margin-bottom: 0 ;
  margin-left: 1.5em }

div.sidebar {
  margin: 0 0 0.5em 1em ;
  border: medium outset ;
  padding: 1em ;
  background-color: #ffffee ;
  width: 40% ;
  float: right ;
  clear: right }

div.sidebar p.rubric {
  font-family: sans-serif ;
  font-size: medium }

div.system-messages {
  margin: 5em }

div.system-messages h1 {
  color: red }

div.system-message {
  border: medium outset ;
  padding: 1em }

div.system-message p.system-message-title {
  color: red ;
  font-weight: bold }

div.topic {
  margin: 2em }

h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
  margin-top: 0.4em }

h1.title {
  text-align: center }

h2.subtitle {
  text-align: center }

hr.docutils {
  width: 75% }

img.align-left {
  clear: left }

img.align-right {
  clear: right }

ol.simple, ul.simple {
  margin-bottom: 1em }

ol.arabic {
  list-style: decimal }

ol.loweralpha {
  list-style: lower-alpha }

ol.upperalpha {
  list-style: upper-alpha }

ol.lowerroman {
  list-style: lower-roman }

ol.upperroman {
  list-style: upper-roman }

p.attribution {
  text-align: right ;
  margin-left: 50% }

p.caption {
  font-style: italic }

p.credits {
  font-style: italic ;
  font-size: smaller }

p.label {
  white-space: nowrap }

p.rubric {
  font-weight: bold ;
  font-size: larger ;
  color: maroon ;
  text-align: center }

p.sidebar-title {
  font-family: sans-serif ;
  font-weight: bold ;
  font-size: larger }

p.sidebar-subtitle {
  font-family: sans-serif ;
  font-weight: bold }

p.topic-title {
  font-weight: bold }

pre.address {
  margin-bottom: 0 ;
  margin-top: 0 ;
  font-family: serif ;
  font-size: 100% }

pre.literal-block, pre.doctest-block {
  margin-left: 2em ;
  margin-right: 2em; overflow-y:scroll; background-color: #CCC
}

span.classifier {
  font-family: sans-serif ;
  font-style: oblique }

span.classifier-delimiter {
  font-family: sans-serif ;
  font-weight: bold }

span.interpreted {
  font-family: sans-serif }

span.option {
  white-space: nowrap }

span.pre {
  white-space: pre }

span.problematic {
  color: red }

span.section-subtitle {
  /* font-size relative to parent (h1..h6 element) */
  font-size: 80% }

table.citation {
  border-left: solid 1px gray;
  margin-left: 1px }

table.docinfo {
  margin: 2em 4em }

table.docutils {
  margin-top: 0.5em ;
  margin-bottom: 0.5em }

table.footnote {
  border-left: solid 1px black;
  margin-left: 1px }

table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
  padding-left: 0.5em ;
  padding-right: 0.5em ;
  vertical-align: top }

table.docutils th.field-name, table.docinfo th.docinfo-name {
  font-weight: bold ;
  text-align: left ;
  white-space: nowrap ;
  padding-left: 0 }

h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
  font-size: 100% }

ul.auto-toc {
  list-style-type: none }

/* Indent the code block */
/* --------------------- */

/* Content copied from the `html4css1.css` rule for literal blocks. */
/* Selector adapted to the output of Pygments_. :: */

div.highlight {
  margin-left: 2em ;
  margin-right: 2em ;
  background-color: #eeeeee
  }


/* Colour code blocks */
/* ------------------ */

/* Pygments_ has an option to generate stylesheets for html and latex. */
/* The following code is generated with the command */
/* `pygmentize -S default -f html > pygments-default.css`:: */

.c { color: #008800; font-style: italic } /* Comment */
.err { border: 1px solid #FF0000 } /* Error */
.k { color: #AA22FF; font-weight: bold } /* Keyword */
.o { color: #666666 } /* Operator */
.cm { color: #008800; font-style: italic } /* Comment.Multiline */
.cp { color: #008800 } /* Comment.Preproc */
.c1 { color: #008800; font-style: italic } /* Comment.Single */
.gd { color: #A00000 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #FF0000 } /* Generic.Error */
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
.gi { color: #00A000 } /* Generic.Inserted */
.go { color: #808080 } /* Generic.Output */
.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #0040D0 } /* Generic.Traceback */
.kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
.kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
.kp { color: #AA22FF } /* Keyword.Pseudo */
.kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
.kt { color: #AA22FF; font-weight: bold } /* Keyword.Type */
.m { color: #666666 } /* Literal.Number */
.s { color: #BB4444 } /* Literal.String */
.na { color: #BB4444 } /* Name.Attribute */
.nb { color: #AA22FF } /* Name.Builtin */
.nc { color: #0000FF } /* Name.Class */
.no { color: #880000 } /* Name.Constant */
.nd { color: #AA22FF } /* Name.Decorator */
.ni { color: #999999; font-weight: bold } /* Name.Entity */
.ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.nf { color: #00A000 } /* Name.Function */
.nl { color: #A0A000 } /* Name.Label */
.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.nt { color: #008000; font-weight: bold } /* Name.Tag */
.nv { color: #B8860B } /* Name.Variable */
.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.mf { color: #666666 } /* Literal.Number.Float */
.mh { color: #666666 } /* Literal.Number.Hex */
.mi { color: #666666 } /* Literal.Number.Integer */
.mo { color: #666666 } /* Literal.Number.Oct */
.sb { color: #BB4444 } /* Literal.String.Backtick */
.sc { color: #BB4444 } /* Literal.String.Char */
.sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
.s2 { color: #BB4444 } /* Literal.String.Double */
.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.sh { color: #BB4444 } /* Literal.String.Heredoc */
.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.sx { color: #008000 } /* Literal.String.Other */
.sr { color: #BB6688 } /* Literal.String.Regex */
.s1 { color: #BB4444 } /* Literal.String.Single */
.ss { color: #B8860B } /* Literal.String.Symbol */
.bp { color: #AA22FF } /* Name.Builtin.Pseudo */
.vc { color: #B8860B } /* Name.Variable.Class */
.vg { color: #B8860B } /* Name.Variable.Global */
.vi { color: #B8860B } /* Name.Variable.Instance */
.il { color: #666666 } /* Literal.Number.Integer.Long */

/* .. _pygments:  http://pygments.org/ */

</style>
</head>
<body>
<div class="document">


<p>On the App Engine Google group, <a class="reference external" href="http://groups.google.com/group/google-appengine/browse_thread/thread/c9e8a906a88a8102">Thomas Kuczek
asked</a>:</p>
<blockquote>
I have a query object representing a query. How can I print the
resulting Gql to log it with the logger framework?</blockquote>
<p>I thought this sounded like an interesting and possibly useful thing
to do, so I wrote a small module which can convert either a db.Query or
a db.GqlQuery object to a meaningful string.</p>
<div class="warning">
<p class="first admonition-title">Warning</p>
<p class="last">This code depends on details of the query implementation (a lot of
the required fields are private), so I can be fairly safe in saying
that not only may it break in the next release of the App Engine
environment, but it almost certainly will break.</p>
</div>
<p>In the code samples which follow the test class is:</p>
<pre class="code-block python literal-block">
<span class="k">class</span> <span class="nc">Person</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Dummy class for testing&quot;&quot;&quot;</span>
    <span class="n">first_name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">StringProperty</span><span class="p">()</span>
    <span class="n">last_name</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">StringProperty</span><span class="p">()</span>
    <span class="n">city</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">StringProperty</span><span class="p">()</span>
    <span class="n">birth_year</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">IntegerProperty</span><span class="p">()</span>
    <span class="n">height</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">IntegerProperty</span><span class="p">()</span>

</pre>
<p>First some setup code for the tests:</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">from</span> <span class="nn">google.appengine.ext</span> <span class="k">import</span> <span class="n">db</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">from</span> <span class="nn">Person</span> <span class="k">import</span> <span class="n">Person</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">from</span> <span class="nn">showquery</span> <span class="k">import</span> <span class="n">showQuery</span><span class="p">,</span> <span class="n">showGqlQuery</span>

</pre>
<p>Now try printing some queries:</p>
<pre class="code-block pycon literal-block">
<span class="n"></span><span class="gp">&gt;&gt;&gt; </span><span class="n">q</span> <span class="o">=</span> <span class="n">Person</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showQuery</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="go">Person.all()
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showQuery</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">&quot;last_name =&quot;</span><span class="p">,</span> <span class="s">&quot;Smith&quot;</span><span class="p">))</span>
<span class="go">Person.all().filter('last_name =', 'Smith')
</span>
</pre>
<p>If we have more than one filter, showQuery will always output them in
sorted order rather than the order in which they were input. This is
simply to make doctests easier:</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showQuery</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s">'height &lt;'</span><span class="p">,</span> <span class="mf">72</span><span class="p">))</span>
<span class="go">Person.all().filter('height &lt;', 72).filter('last_name =', 'Smith')
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showQuery</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="s">&quot;-height&quot;</span><span class="p">))</span>
<span class="go">Person.all().filter('height &lt;', 72).filter('last_name =', 'Smith').order('-height')
</span>
</pre>
<p>We can even handle an ancestor on the query although the key may be a
bit of a mouthful:</p>
<pre class="code-block pycon literal-block">
<span class="n"></span><span class="gp">&gt;&gt;&gt; </span><span class="n">p</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="n">first_name</span><span class="o">=</span><span class="s">'Duncan'</span><span class="p">,</span> <span class="n">last_name</span><span class="o">=</span><span class="s">'Booth'</span><span class="p">,</span> <span class="n">city</span><span class="o">=</span><span class="s">'Oxford'</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">183</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">key</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">put</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showQuery</span><span class="p">(</span><span class="n">q</span><span class="o">.</span><span class="n">ancestor</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
<span class="go">Person.all().ancestor(datastore_types.Key.from_path('Person', 1, _app=u'test_app')).filter('height &lt;', 72).filter('last_name =', 'Smith').order('-height')
</span>
</pre>
<p>There is also a <tt class="docutils literal"><span class="pre">showGqlQuery</span></tt> function to convert GQL back to the
equivalent query:</p>
<pre class="code-block pycon literal-block">
<span class="n"></span><span class="gp">&gt;&gt;&gt; </span><span class="n">q</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE last_name = :1 AND height &lt; :2&quot;</span><span class="p">,</span> <span class="s">&quot;Smith&quot;</span><span class="p">,</span> <span class="mf">72</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="go">SELECT * FROM Person WHERE last_name = :1 AND height &lt; :2
</span>
</pre>
<p>Notice that once again the output may have the clauses in a different
order than they were originally input.</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE last_name = :name AND height &lt; :height&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE height &lt; :height AND last_name = :name
</span>
</pre>
<p>We can also handle literal values in queries:</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE last_name = 'Smith'&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE last_name = 'Smith'
</span>
</pre>
<p>Sorting is also handled. The ORDER BY clause does preserve the
original order:</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE height &lt; :1 ORDER BY last_name ASC&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE height &lt; :1 ORDER BY last_name ASC
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE height&lt;:1 ORDER BY last_name DESC, height ASC&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE height &lt; :1 ORDER BY last_name DESC, height ASC
</span>
</pre>
<p>Ancestor, limit, and offset classes also all work. If you specify
limit and offset separately then they are output together:</p>
<pre class="code-block pycon literal-block">
<span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE ANCESTOR IS :1 AND height &lt; 72&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE ANCESTOR IS :1 AND height &lt; 72
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE ANCESTOR IS :1 LIMIT 10,5&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE ANCESTOR IS :1 LIMIT 10,5
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE ANCESTOR IS :1 OFFSET 3&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE ANCESTOR IS :1 OFFSET 3
</span><span class="k"></span><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">showGqlQuery</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">GqlQuery</span><span class="p">(</span><span class="s">&quot;SELECT * FROM Person WHERE ANCESTOR IS 'xxx' LIMIT 3 OFFSET 5&quot;</span><span class="p">))</span>
<span class="go">SELECT * FROM Person WHERE ANCESTOR IS 'xxx' LIMIT 5,3
</span>
</pre>
<p>The source code (showquery.py) looks like this:</p>
<pre class="code-block python literal-block">
<span class="k">from</span> <span class="nn">google.appengine.ext</span> <span class="k">import</span> <span class="n">db</span>
<span class="k">from</span> <span class="nn">google.appengine.api</span> <span class="k">import</span> <span class="n">datastore</span>

<span class="k">def</span> <span class="nf">showQuery</span><span class="p">(</span><span class="n">query</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Represent a query as a string&quot;&quot;&quot;</span>
    <span class="n">kind</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_model_class</span><span class="o">.</span><span class="n">kind</span><span class="p">()</span>
    <span class="n">ancestor</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_Query__ancestor</span>
    <span class="n">filters</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_Query__query_set</span>
    <span class="n">orderings</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_Query__orderings</span>
    <span class="n">hint</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">limit</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">offset</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="n">res</span> <span class="o">=</span> <span class="p">[</span><span class="s">&quot;</span><span class="si">%s</span><span class="s">.all()&quot;</span> <span class="o">%</span> <span class="n">kind</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">ancestor</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;ancestor(</span><span class="si">%r</span><span class="s">)&quot;</span> <span class="o">%</span> <span class="n">ancestor</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">sorted</span><span class="p">(</span><span class="n">filters</span><span class="p">):</span>
        <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;filter(</span><span class="si">%r</span><span class="s">, </span><span class="si">%r</span><span class="s">)&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">filters</span><span class="p">[</span><span class="n">k</span><span class="p">]))</span>
    <span class="k">for</span> <span class="n">p</span><span class="p">,</span> <span class="n">o</span> <span class="ow">in</span> <span class="n">orderings</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">o</span><span class="o">==</span><span class="n">datastore</span><span class="o">.</span><span class="n">Query</span><span class="o">.</span><span class="n">DESCENDING</span><span class="p">:</span>
            <span class="n">p</span> <span class="o">=</span> <span class="s">'-'</span><span class="o">+</span><span class="n">p</span>
        <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;order(</span><span class="si">%r</span><span class="s">)&quot;</span> <span class="o">%</span> <span class="n">p</span><span class="p">)</span>

    <span class="k">return</span> <span class="s">'.'</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">showGqlQuery</span><span class="p">(</span><span class="n">query</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Represent a GQL query as a string&quot;&quot;&quot;</span>
    <span class="n">proto</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_proto_query</span>
    <span class="n">kind</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">_model_class</span><span class="o">.</span><span class="n">kind</span><span class="p">()</span>
    <span class="n">filters</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">filters</span><span class="p">()</span>
    <span class="n">boundfilters</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">_GQL__bound_filters</span>
    <span class="n">orderings</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">orderings</span><span class="p">()</span>
    <span class="n">hint</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">hint</span><span class="p">()</span>
    <span class="n">limit</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">limit</span><span class="p">()</span>
    <span class="n">offset</span> <span class="o">=</span> <span class="n">proto</span><span class="o">.</span><span class="n">_GQL__offset</span>

    <span class="n">select</span> <span class="o">=</span> <span class="s">&quot;SELECT * FROM </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="n">kind</span>
    <span class="n">where</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">order</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">sorted</span><span class="p">(</span><span class="n">filters</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">clause</span> <span class="ow">in</span> <span class="n">filters</span><span class="p">[</span><span class="n">k</span><span class="p">]:</span>
            <span class="n">name</span><span class="p">,</span> <span class="n">op</span> <span class="o">=</span> <span class="n">clause</span>
            <span class="k">if</span> <span class="n">name</span><span class="o">==-</span><span class="mf">1</span><span class="p">:</span> <span class="n">name</span> <span class="o">=</span> <span class="s">'ANCESTOR'</span>
            <span class="n">where</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;</span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s"> :</span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">op</span><span class="o">.</span><span class="n">upper</span><span class="p">(),</span> <span class="n">k</span><span class="p">))</span>

    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">sorted</span><span class="p">(</span><span class="n">boundfilters</span><span class="p">):</span>
        <span class="n">where</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;</span><span class="si">%s</span><span class="s"> </span><span class="si">%r</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">boundfilters</span><span class="p">[</span><span class="n">k</span><span class="p">]))</span>

    <span class="k">for</span> <span class="n">p</span><span class="p">,</span> <span class="n">o</span> <span class="ow">in</span> <span class="n">orderings</span><span class="p">:</span>
        <span class="n">order</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&quot;</span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s">'DESC'</span> <span class="k">if</span> <span class="n">o</span><span class="o">==</span><span class="n">datastore</span><span class="o">.</span><span class="n">Query</span><span class="o">.</span><span class="n">DESCENDING</span> <span class="k">else</span> <span class="s">'ASC'</span><span class="p">))</span>

    <span class="n">gql</span> <span class="o">=</span> <span class="n">select</span>
    <span class="k">if</span> <span class="n">where</span><span class="p">:</span>
        <span class="n">gql</span> <span class="o">+=</span> <span class="s">' WHERE '</span><span class="o">+</span><span class="s">' AND '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">where</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">order</span><span class="p">:</span>
        <span class="n">gql</span> <span class="o">+=</span> <span class="s">' ORDER BY '</span> <span class="o">+</span> <span class="s">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">limit</span> <span class="o">!=</span> <span class="o">-</span><span class="mf">1</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">offset</span> <span class="o">!=</span> <span class="o">-</span><span class="mf">1</span><span class="p">:</span>
            <span class="n">gql</span> <span class="o">+=</span> <span class="s">' LIMIT </span><span class="si">%s</span><span class="s">,</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">offset</span><span class="p">,</span><span class="n">limit</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">gql</span> <span class="o">+=</span> <span class="s">' LIMIT </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">limit</span>
    <span class="k">elif</span> <span class="n">offset</span> <span class="o">!=</span> <span class="o">-</span><span class="mf">1</span><span class="p">:</span>
            <span class="n">gql</span> <span class="o">+=</span> <span class="s">' OFFSET </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">offset</span>
    <span class="k">return</span> <span class="n">gql</span>

</pre>
</div>
</body>
</html>
